﻿namespace Atomic.Mvc
{
    using System;
    using System.Linq;
    using System.Collections.Generic;
    using System.Reflection;
    using System.Web.Mvc;
    using System.Web.Routing;

    using Atomic.Exceptions;
    using Atomic.Extensions;
    using Atomic.Plugins;
    using Atomic.Plugins.Files;
    using Atomic.Mvc;
    using Atomic.Mvc.Themes;
    using Atomic.Mvc.Properties;
    using Atomic.Mvc.Routes;
    using Atomic.Mvc.Controllers;
    using Atomic.Mvc.ViewEngines;

    /// <summary>
    /// Web 插件初始化程序。
    /// </summary>
    public class WebPluginInitializer : IPluginInitializer
    {
        /// <summary>
        /// 程序集探测目录。
        /// </summary>
        private readonly IAssemblyProbingFolder _assemblyProbingFolder = null;

        /// <summary>
        /// 布局管理器。
        /// </summary>
        private readonly ILayoutManager _layoutManager = null;

        /// <summary>
        /// 部件管理器。
        /// </summary>
        private readonly IPartManager _partManager = null;

        /// <summary>
        /// Web 插件初始化程序。
        /// </summary>
        /// <param name="assemblyProbingFolder">程序集探测目录。</param>
        /// <param name="layoutManager">布局管理器。</param>
        /// <param name="partManager">部件管理器。</param>
        public WebPluginInitializer(IAssemblyProbingFolder assemblyProbingFolder, ILayoutManager layoutManager, IPartManager partManager)
        {
            this._assemblyProbingFolder = assemblyProbingFolder;
            this._layoutManager = layoutManager;
            this._partManager = partManager;
        }

        /// <summary>
        /// 初始化。
        /// </summary>
        /// <param name="pluginDescriptor">插件描述信息。</param>
        public void Initialize(PluginDescriptor pluginDescriptor)
        {
            var assembly = this._assemblyProbingFolder.LoadAssembly(pluginDescriptor.Dependency.Name);

            if (assembly != null)
            {
                AtomicContainer.Container.RegisterAssemblyTypesAsImplementedInterfaces(assembly);

                this.RegisterControllers(pluginDescriptor.Id, assembly);
                this.RegisterRolutes(pluginDescriptor, assembly);
                this.RegisterModelBinders(pluginDescriptor, assembly);
                this.RegisterLayout(this._layoutManager, pluginDescriptor, assembly);
                this.RegisterPart(this._partManager, pluginDescriptor, assembly);

                this.InitializePlugin(assembly);
            }
        }

        #region 注册控制器

        /// <summary>
        /// 注册 MVC 控制器。
        /// </summary>
        /// <param name="pluginId">插件唯一标识。</param>
        /// <param name="assembly">插件程序集。</param>
        private void RegisterControllers(string pluginId, Assembly assembly)
        {
            assembly.GetTypes()
                    .Where(type => typeof(Controller).IsAssignableFrom(type) && type.IsClass && !type.IsAbstract)
                    .ForEach(type => this.RegisterController(pluginId, type));
        }

        /// <summary>
        /// 注册控制器。
        /// </summary>
        /// <param name="pluginId">插件唯一标识。</param>
        /// <param name="controllerType">控制器类型。</param>
        private void RegisterController(string pluginId, Type controllerType)
        {
            if (controllerType != null)
            {
                AtomicContainer.Container.RegisterType(controllerType);

                string key = pluginId + "-" + controllerType.Name;

                if (!AtomicControllerFactory.ControllerTypes.ContainsKey(key))
                {
                    AtomicControllerFactory.ControllerTypes.Add(key, controllerType);
                }
                else
                {
                    throw new AtomicException(string.Format("控制器名称 {0} 已存在。", controllerType.Name));
                }
            }
        }

        #endregion

        #region 注册路由规则

        /// <summary>
        /// 注册路由规则。
        /// </summary>
        /// <param name="pluginDescriptor">插件描述信息。</param>
        /// <param name="assembly">插件程序集。</param>
        private void RegisterRolutes(PluginDescriptor pluginDescriptor, Assembly assembly)
        {
            assembly.GetTypes()
                    .Where(type => typeof(IRouteProvider).IsAssignableFrom(type) && type.IsClass && !type.IsAbstract)
                    .ForEach(type => this.RegisterRolutes(pluginDescriptor, type));
        }

        /// <summary>
        /// 注册路由规则。
        /// </summary>
        /// <param name="pluginDescriptor">插件描述信息。</param>
        /// <param name="type">路由规则提供程序实现类型。</param>
        private void RegisterRolutes(PluginDescriptor pluginDescriptor, Type type)
        {
            if (type != null)
            {
                var routeProvider = (IRouteProvider)Activator.CreateInstance(type);

                if (routeProvider != null)
                {
                    this.RegisterRoute(pluginDescriptor, RouteTable.Routes, routeProvider.GetRoutes());
                }
            }
        }

        /// <summary>
        /// 注册路由规则到 MVC 中。
        /// </summary>
        /// <param name="pluginDescriptor">插件信息描述。</param>
        /// <param name="context">MVC 路由规则上下文</param>
        /// <param name="routes">路由规则描述信息集合</param>
        private void RegisterRoute(PluginDescriptor pluginDescriptor, RouteCollection context, IEnumerable<RouteDescriptor> routes)
        {
            routes.ForEach(route => this.RegisterRoute(pluginDescriptor, context, route));
        }

        /// <summary>
        /// 注册路由规则到 MVC 中
        /// </summary>
        /// <param name="pluginDescriptor">插件信息描述。</param>
        /// <param name="context">MVC 路由规则上下文</param>
        /// <param name="route">路由规则描述信息</param>
        private void RegisterRoute(PluginDescriptor pluginDescriptor, RouteCollection context, RouteDescriptor route)
        {
            if (route.Name.IsNullOrEmpty())
                throw new ArgumentException("路由规则 Name 不能为空。");

            if (route.Url.IsNull())
                throw new ArgumentException("路由规则 RUL 不能为空。");

            Route route2 = new Route(route.Url, new MvcRouteHandler())
            {
                Defaults = new RouteValueDictionary(route.Defaults),
                Constraints = new RouteValueDictionary(route.Constraints),
                DataTokens = new RouteValueDictionary()
            };

            if (route.Area)
            {
                route2.Defaults[Areas.Area] = pluginDescriptor.Id;
                route2.DataTokens[Areas.Area] = pluginDescriptor.Id;
                route2.Defaults[Resources.VIEW_PLUGIN_NAME] = pluginDescriptor.Id;
            }
            else if (!route2.Defaults.ContainsKey(Resources.VIEW_PLUGIN_NAME))
            {
                route2.Defaults.Add(Resources.VIEW_PLUGIN_NAME, pluginDescriptor.Id);
            }

            route2.Defaults[Resources.VIEW_PLUGIN_FOLDER] = GetPluginFolder(pluginDescriptor.Location);

            Route item = route2;

            if ((route.Namespaces != null) && (route.Namespaces.Length > 0))
            {
                item.DataTokens["Namespaces"] = route.Namespaces;
            }

            context.Add(route.Name, item);
        }

        /// <summary>
        /// 卸载路由规则
        /// </summary>
        /// <param name="context">MVC 路由规则上下文</param>
        /// <param name="routes">路由规则描述信息集合</param>
        private void UnloadRoute(RouteCollection context, IEnumerable<RouteDescriptor> routes)
        {
            if (routes != null && routes.Count() > 0)
            {
                foreach (var route in routes)
                {
                    if (route.Name.IsNullOrEmpty())
                        throw new ArgumentException("路由规则 Name 不能为空。");

                    if (route.Url.IsNullOrEmpty())
                        throw new ArgumentException("路由规则 RUL 不能为空。");

                    var mvcRoutes = context.ToList().FirstOrDefault(r => ((Route)r).Url == route.Url);
                    context.Remove(mvcRoutes);
                }
            }
        }

        /// <summary>
        /// 获得插件目录名称。
        /// </summary>
        /// <param name="location"></param>
        /// <returns></returns>
        private string GetPluginFolder(string location)
        {
            if (location.StartsWith("~/"))
            {
                return location.Substring(2);
            }

            return location;
        }

        #endregion

        #region 注册模型绑定器

        /// <summary>
        /// 注册模型绑定器。
        /// </summary>
        /// <param name="pluginDescriptor">插件描述信息。</param>
        /// <param name="assembly">插件程序集。</param>
        private void RegisterModelBinders(PluginDescriptor pluginDescriptor, Assembly assembly)
        {
            assembly.GetTypes()
                    .Where(type => typeof(Atomic.Mvc.ModelBinders.IModelBinderProvider).IsAssignableFrom(type) && type.IsClass && !type.IsAbstract)
                    .ForEach(type => this.RegisterModelBinders(pluginDescriptor, type));
        }

        /// <summary>
        /// 注册模型绑定器。
        /// </summary>
        /// <param name="pluginDescriptor">插件描述信息。</param>
        /// <param name="type">模型绑定器提供程序实现类型。</param>
        private void RegisterModelBinders(PluginDescriptor pluginDescriptor, Type type)
        {
            if (type != null)
            {
                var modelBinderProvider = (Atomic.Mvc.ModelBinders.IModelBinderProvider)Activator.CreateInstance(type);

                if (modelBinderProvider != null)
                {
                    modelBinderProvider.GetModelBinders().ForEach(modelBinder => System.Web.Mvc.ModelBinders.Binders.Add(modelBinder.Type, modelBinder.ModelBinder));
                }
            }
        }

        #endregion

        #region 注册布局模板

        /// <summary>
        /// 注册布局模板。
        /// </summary>
        /// <param name="layoutManager">布局管理器。</param>
        /// <param name="pluginDescriptor">插件描述信息。</param>
        /// <param name="assembly">插件程序集。</param>
        private void RegisterLayout(ILayoutManager layoutManager, PluginDescriptor pluginDescriptor, Assembly assembly)
        {
            assembly.GetTypes()
                    .Where(type => typeof(ILayoutProvider).IsAssignableFrom(type) && type.IsClass && !type.IsAbstract)
                    .ForEach(type => this.RegisterLayout(layoutManager, type));
        }

        /// <summary>
        /// 注册布局模板。
        /// </summary>
        /// <param name="layoutManager">布局管理器。</param>
        /// <param name="type">布局信息提供程序类型。</param>
        private void RegisterLayout(ILayoutManager layoutManager, Type type)
        {
            if (type != null)
            {
                var layoutProvider = (ILayoutProvider)Activator.CreateInstance(type);

                if (layoutProvider != null)
                {
                    this.RegisterLayout(layoutManager, layoutProvider);
                }
            }
        }

        /// <summary>
        /// 注册布局模板。
        /// </summary>
        /// <param name="layoutManager">布局管理器。</param>
        /// <param name="layoutProvider">布局信息提供程序。</param>
        private void RegisterLayout(ILayoutManager layoutManager, ILayoutProvider layoutProvider)
        {
            layoutProvider.GetLayouts().ForEach(layout => layoutManager.Add(layout));
        }

        #endregion

        #region 注册部件模板

        /// <summary>
        /// 注册部件模板。
        /// </summary>
        /// <param name="partManager">部件管理器。</param>
        /// <param name="pluginDescriptor">插件描述信息。</param>
        /// <param name="assembly">插件程序集。</param>
        private void RegisterPart(IPartManager partManager, PluginDescriptor pluginDescriptor, Assembly assembly)
        {
            assembly.GetTypes()
                    .Where(type => typeof(IPartProvider).IsAssignableFrom(type) && type.IsClass && !type.IsAbstract)
                    .ForEach(type => this.RegisterPart(partManager, type));
        }

        /// <summary>
        /// 注册部件模板。
        /// </summary>
        /// <param name="partManager">部件管理器。</param>
        /// <param name="type">部件信息提供程序类型。</param>
        private void RegisterPart(IPartManager partManager, Type type)
        {
            if (type != null)
            {
                var partProvider = (IPartProvider)Activator.CreateInstance(type);

                if (partProvider != null)
                {
                    this.RegisterPart(partManager, partProvider);
                }
            }
        }

        /// <summary>
        /// 注册部件模板。
        /// </summary>
        /// <param name="partManager">部件管理器。</param>
        /// <param name="partProvider">部件信息提供程序。</param>
        private void RegisterPart(IPartManager partManager, IPartProvider partProvider)
        {
            partProvider.GetParts().ForEach(part => partManager.Add(part));
        }

        #endregion

        #region 初始化插件

        /// <summary>
        /// 初始化插件。
        /// </summary>
        /// <param name="assembly">插件程序集。</param>
        private void InitializePlugin(Assembly assembly)
        {
            assembly.GetTypes()
                    .Where(type => typeof(ISetupPlugin).IsAssignableFrom(type) && type.IsClass && !type.IsAbstract)
                    .ForEach(type => this.InitializePlugin(type));
        }

        /// <summary>
        /// 初始化插件。
        /// </summary>
        /// <param name="type">初始化对象的类型。</param>
        private void InitializePlugin(Type type)
        {
            if (type != null)
            {
                var setup = (ISetupPlugin)Activator.CreateInstance(type);

                if (setup != null)
                {
                    setup.Initialize();
                }
            }
        }

        #endregion
    }
}